package org.swift.mashup.engine.model;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.axiom.attachments.utils.IOUtils;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.swift.commons.xml.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class TransformXsltActivity extends XMLActivity implements Activity {
	
	private final static Logger log = Logger.getLogger(TransformXsltActivity.class);

	private Templates xslt;
	
	public TransformXsltActivity(NodeList returnContents, String match,
			Collection<? extends Parameter> params,
			Map<String, String> namespaces, String encoding) 
			throws TransformerConfigurationException, SAXException {
		super(params, namespaces);
		this.xslt = this.createXslt(returnContents, match, params,encoding);
	}

	@Override
	public ActivityResultComponent doActivity(ActivityContext context,
			Map<String, ? extends Object> parameters) throws ActivityException {
		if (context.getSize() == 0){
			context = NULL_CONTEXT;
		}
		try {								
			String szContext = new String(context.getBytes());			
			if (!XmlUtils.isXml(szContext)){
				szContext = XmlUtils.toXML(szContext);
			}
			return new ActivityResultItem(transform(szContext, parameters));			
		} catch (ActivityException e) {
			throw e;
		} catch (Exception e) {
			throw new ActivityException(e);
		}
	}
	
	private Templates createXslt(NodeList xsltSnipit, String match,
			Collection<? extends Parameter> params, String encoding) throws TransformerConfigurationException, SAXException {		
		return XsltUtil.createTranslet(xsltSnipit, match, Parameter
				.toValueMap(params).keySet(), this.getNamespaces(), encoding);		
	}
	
	private Object transform(String value,
			Map<String, ?> parameters)
			throws TransformerException, IOException, NotSupportedActivityException {
		if (this.xslt == null) return value;
		//TODO insert the Namespaces before transform

		Source source = new StreamSource(new StringReader(value));	

		ByteArrayOutputStream output = new ByteArrayOutputStream();
		StreamResult result = new StreamResult(output);
		Transformer transformer = this.xslt.newTransformer();		
		for (Entry<String, ?> parameter : parameters.entrySet()) {
			transformer.setParameter(parameter.getKey(), toDomParameter(parameter.getValue()));
						
		}
		transformer.transform(source, result);	
		return output.toString();	
	}
	
	private Object toDomParameter(Object object) throws IOException, NotSupportedActivityException {
		//check if it's a string, byte[], file or stream
		
		String string = toString(object); 
		if (XmlUtils.isXml(string)) {
			try {
				return new DOMSource(this.createDocFromString(string));
			} catch (Exception e) {
				log.debug("Thought this was xml, but I can't parse it", e);					
			}
			return string;
		}
 		
		//still could be int, long or other valid parameter. if it's not, we'll let the transform fail.
		return object;		
	}
	
	private Document createDocFromString(String xmlSource) throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();		
	    return builder.parse(new InputSource(new StringReader(xmlSource)));
	}
	
	private String toString(Object object) throws IOException, NotSupportedActivityException {
		if (object instanceof String) {
			return (String) object;
		}
		if (object instanceof byte[]) {
			return new String((byte[])object);
		}
		if (object instanceof File) {
			return FileUtils.readFileToString((File)object);
		}
		if (object instanceof InputStream) {
			return new String(IOUtils.getStreamAsByteArray((InputStream) object));
		}
		//should never happen because of isSupported()
		throw new NotSupportedActivityException(object.getClass().toString());
	}

}
